{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Sorting Network Sets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Frequently a set of `Networks` is recorded while changing some other variable; like voltage, or current or time. So... now you have this set of data  and you want to look at how some feature evolves, or calculate some representative  statistics. This example demonstrates how to do this using [NetworkSets](../../tutorials/NetworkSet.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate some Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the purpose of this example we use a predefined `skrf.Media` object to generate some networks, and save them as a series of touchstone files. Each  file is named with a timestamp, generated with the convenience function `rf.now_string()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from time import sleep\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import skrf as rf\n",
    "\n",
    "%matplotlib inline\n",
    "rf.stylely()\n",
    "\n",
    "\n",
    "!rm -rf tmp\n",
    "!mkdir tmp\n",
    "\n",
    "wg = rf.wr10 # just a dummy media object to generate data\n",
    "\n",
    "for k in range(10):\n",
    "    # timestamp generated with `rf.now_string()`\n",
    "    ntwk = wg.random(name=rf.now_string()+'.s1p')\n",
    "    ntwk.s = k*ntwk.s\n",
    "    ntwk.write_touchstone(dir='tmp')\n",
    "    sleep(.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets take a look at what we made"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!ls tmp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Not sorted (default)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When created using `NetworkSet.from_dir()`, the `NetworkSet`'s stores each `Network` randomly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns = rf.NS.from_dir('tmp')\n",
    "ns.ntwk_set"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sort it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sort()\n",
    "ns.ntwk_set"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sorting using `key` argument "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also pass a function through the `key` argument, which allows you to sort on arbitrary properties. For example, we could sort based on the sub-second field of the name,  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns = rf.NetworkSet.from_dir('tmp')\n",
    "ns.sort(key = lambda x: x.name.split('.')[0])\n",
    "ns.ntwk_set"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting Datetimes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also convert the ntwk names to datetime objects, in case you want to plot something with pandas or do some other processing. There is a companion function to `rf.now_string()` which is `rf.now_string_2_dt()`. How creative.."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sort()\n",
    "dt_idx = [rf.now_string_2_dt(k.name ) for k in ns]\n",
    "dt_idx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Put into a Pandas DataFrame and Plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to slice the network set along the time axis. For example we may want to look at S11 phase, at a few different frequencies. This can be done with the following script. Note that NetworkSets can be sliced by frequency with human readable strings, just like Networks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "dates = pd.DatetimeIndex(dt_idx)\n",
    "\n",
    "# create a function to pull out S11 in degrees at a specific frequency\n",
    "\n",
    "s_deg_at = lambda s:{s: [k[s].s_deg[0,0,0] for k in ns]}  # noqa: E731\n",
    "\n",
    "for f in ['80ghz', '90ghz','100ghz']:\n",
    "    df =pd.DataFrame(s_deg_at(f), index=dates)\n",
    "    df.plot(ax=plt.gca())\n",
    "plt.title('Phase Evolution in Time')\n",
    "plt.ylabel('S11 (deg)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualizing Behavior with  `signature`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It may be of use to visualize the evolution of a scalar component of the network set over all frequencies. This can be done with a little bit of array manipulation and `imshow`. For example if we take the magnitude in dB for each network, and create  2D matrix from this,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mat = np.array([k.s_db.flatten() for k in ns])\n",
    "mat.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This array has shape  ( 'Number of Networks' , 'Number frequency points'). This can be visualized with imshow. Most of the code below just adds labels, and axis-scales. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "freq = ns[0].frequency\n",
    "\n",
    "# creates x and y scales\n",
    "extent = [freq.f_scaled[0], freq.f_scaled[-1], len(ns) ,0]\n",
    "\n",
    "#make the image\n",
    "plt.imshow(mat, aspect='auto',extent=extent,interpolation='nearest')\n",
    "\n",
    "# label things\n",
    "plt.grid(0)\n",
    "freq.labelXAxis()\n",
    "plt.ylabel('Network #')\n",
    "cbar = plt.colorbar()\n",
    "cbar.set_label('Magnitude (dB)')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This process is automated with the method `NetworkSet.signature()`. It even has a `vs_time` parameter which will automatically create the DateTime index from the Network's names, if they were written by `rf.now_string()` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.signature(component='s_db', vs_time=True,cbar_label='Magnitude (dB)')"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
